/**
 * 
 */
package com.henryzhou.cloud.google;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

import com.henryzhou.cloud.Context;
import com.henryzhou.cloud.IGetMethod;
import com.henryzhou.cloud.IHttpMethod;
import com.henryzhou.cloud.IRequestBuilder;
import com.henryzhou.cloud.request.GetRequest;
import com.henryzhou.cloud.request.IRequest;

/**
 * @author haozhou
 *
 */
public class GoogleRequestBuilder implements IRequestBuilder {
	private Context ctx;
	
	public Context getCtx() {
		return ctx;
	}

	public void setCtx(Context ctx) {
		this.ctx = ctx;
	}
	
	protected String getCurrentDate() {
		SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
		return fmt.format(new Date());
	}
	
	protected String getMessage(String httpMethod, String hash, String contentType, String bucket, String object, Map<String, String> headers) {
		StringBuffer buffer = new StringBuffer();
		buffer.append(httpMethod.toUpperCase());
		buffer.append(GoogleContext.NEWLINE);
		if (hash != null) {
			buffer.append(hash);
		}
		buffer.append(GoogleContext.NEWLINE);
		if (contentType != null) {
			buffer.append(contentType);
		}
		buffer.append(GoogleContext.NEWLINE);
		buffer.append(GoogleContext.NEWLINE);//skip date;
		
		ArrayList<String> keys = new ArrayList<String>();
        keys.addAll(headers.keySet());
        Collections.sort(keys);
        for( String hkey : keys ) {
            if( hkey.startsWith("x-amz") || hkey.startsWith("x-goog")) {
                String val = headers.get(hkey);
                
                if( val != null ) {
                	buffer.append(hkey);
                	buffer.append(":");
                	buffer.append(headers.get(hkey).trim());
                	buffer.append("\n");
                }
            }
        }
		
		
		buffer.append("/");
		if (bucket != null ) {
			buffer.append(bucket.toLowerCase());
			buffer.append("/");
        }
        if (object != null ) {
        	if (object.indexOf("?") > -1) {
        		buffer.append(object.substring(0, object.indexOf("?")));
        	} else {
        		buffer.append(object.toLowerCase());
        	}
        }
		
		return buffer.toString();
	}
	
	protected String getAuthentication(IHttpMethod method, Map<String, String> headers) {
		StringBuffer buffer = new StringBuffer();
		buffer.append("GOOG1 ");
		buffer.append(ctx.getAccessKey());
		buffer.append(":");
		buffer.append(signMessage((getMessage(method.getHttpMethod(), method.getHash(), method.getContentType(), method.getBucket(), method.getObject(), headers))));
		return buffer.toString();
	}
	
	private String getEndpoint(IHttpMethod method) {
		if (method.getObject() == null || method.getObject().trim().length() == 0) {
			return getCtx().getHost();
		} else {
			StringBuffer buffer = new StringBuffer();
			String object = method.getObject();
			buffer.append(getCtx().getHost());
			if (object != null && object.startsWith("/")) {
				buffer.append(method.getObject());
			} else {
				buffer.append("/");
				buffer.append(object);
			}
			return buffer.toString();
		}
	}

	@Override
	public IRequest accept(IGetMethod method) {
		Map<String, String> headers = new HashMap<String, String>(); 
		headers.put(GoogleContext.PHOST, getCtx().getHost().replace("http://", ""));
		headers.put(GoogleContext.PDATE, getCurrentDate());
		headers.put(GoogleContext.PAUTH, getAuthentication(method, headers));
		
		GetRequest req = new GetRequest(getEndpoint(method), headers);
		return req;
	}
	
	public String signMessage(String data) {
		String result;
		try {
			SecretKeySpec signingKey = new SecretKeySpec(getCtx().getSecretKey()
					.getBytes("UTF-8"), getCtx().getAlgorithm());

			Mac mac = Mac.getInstance(getCtx().getAlgorithm());
			mac.init(signingKey);

			// compute the hmac on input data bytes
			byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));

			// base64-encode the hmac
			result = new String(Base64.encodeBase64(rawHmac));
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("Failed to generate HMAC : "
					+ e.getMessage());
		}
		return result;
	}

}
